/* 
 * Author: vdaras
 */

#include <algorithm>

#include "../sdl/Color.h"
#include "../MouseEvent.h"
#include "../KeyboardEvent.h"
#include "TextBox.h"
#include "Graphics.h"
#include "ScrollableText.h"

const int BOUNDING_RECT_OFFSET = 3;

namespace gui
{


TextBox::TextBox(const std::string &name, int w, int h, sdl::Font *fnt)
:
Component(name, w, h),
m_font(fnt)
{
    //set the Text Box's height as the biggest value between the h parameter
    //and the height of the font plus the space for the bounding rect
    int height = std::max( h, fnt->GetHeight( ) + 2 * BOUNDING_RECT_OFFSET );
    SetHeight( height );

    //allocate an empty white surface with a same size as the Text Box
    m_background.reset( new sdl::Canvas( w, height, sdl::Colors::WHITE ) );

    //draw a black bounding rectangle on the allocated surface
    gfx::drawRect( m_background.get( ), 0, 0, w - 1, height - 1, sdl::Colors::BLACK );
    gfx::drawRect( m_background.get( ), 1, 1, w - BOUNDING_RECT_OFFSET, height - BOUNDING_RECT_OFFSET, sdl::Colors::BLACK );

    m_scrollingEnabled = false;
    m_text.reset( new Text );
}


TextBox::TextBox(const std::string& name, Container* parent, int x, int y, int w, int h, sdl::Font* fnt)
:
Component(name, parent, x, y, w, h),
m_font(fnt)
{
    //set the Text Box's height as the biggest value between the h parameter
    //and the height of the font plus the space for the bounding rect
    int height = std::max( h, fnt->GetHeight( ) + 2 * BOUNDING_RECT_OFFSET );
    SetHeight( height );

    //allocate an empty white surface with a same size as the Text Box
    m_background.reset( new sdl::Canvas( w, height, sdl::Colors::WHITE ) );

    //draw a black bounding rectangle on the allocated surface
    gfx::drawRect( m_background.get( ), 0, 0, w - 1, height - 1, sdl::Colors::BLACK );
    gfx::drawRect( m_background.get( ), 1, 1, w - BOUNDING_RECT_OFFSET, height - BOUNDING_RECT_OFFSET, sdl::Colors::BLACK );

    m_scrollingEnabled = false;
    m_text.reset( new Text );
}


TextBox::~TextBox()
{
}


/**
 * Resizes this TextBox.
 *
 * @param width: new width.
 * @param height: new height.
 */

void TextBox::Resize(int width, int height)
{
    
}


/**
 * Returns the Text Box's current text.
 *
 * @return a string containing text entered.
 */

std::string TextBox::GetText() const
{
    return m_text->GetCurrentRow( );
}


/**
 * Enables scrolling for this textbox.
 */

void TextBox::EnableScrolling()
{
    //if scrlling is disabled
    if( !m_scrollingEnabled )
    {
        //substitute the current text object with a scrollable text object
        m_text.reset( new ScrollableText );

        //flag tha scrolling is enabled
        m_scrollingEnabled = true;
    }
}


/**
 * Disables scrolling for this textbox.
 */

void TextBox::DisableScrolling()
{
    //if scrolling is enabled
    if( m_scrollingEnabled )
    {
        //get previous contents of the text box
        std::string contents = m_text->GetContents( );

        //substitute the current text object with a regular text object
        m_text.reset( new Text( contents ) );

        //flag tha scrolling is disabled
        m_scrollingEnabled = false;
    }
}


/**
 * Draws the Text Box on the target Surface.
 *
 * @param target: target Surface.
 */

void TextBox::Draw(sdl::Surface* target) const
{
    //draw background
    m_background->Blit( GetX( ), GetY( ), target, &m_visiblePart );

    //draw text
    std::string text;

    //if scrolling is enabled
    if( m_scrollingEnabled )
    {
        //get only the viewable part of the current row
        ScrollableText *txt = dynamic_cast < ScrollableText* > ( m_text.get( ) );
        text = txt->getViewableRow( );
    }
    else
    {
        text = m_text->GetCurrentRow( );
    }

    //render text by clipping only the txt box's area from the rendered text image
    static SDL_Rect txt_clip = {0, 0, GetWidth( ), GetHeight( )};
    
    std::auto_ptr< sdl::TextImage > txtDisplay = m_font->GetImage( text, sdl::Colors::BLACK );

    //if the text box has focus
    if( m_hasFocus )
    {
        //draw cursor on text
        int cursorX = m_text->GetCursorX( m_font ) + GetX( );
        int cursorY = m_text->GetCursorY( m_font ) + GetY( );
        if( cursorX < GetX( ) + GetWidth( ) )
        {
            gfx::drawLine( target, cursorX, cursorY, cursorX, cursorY + m_font->GetHeight( ), sdl::Colors::BLACK );
        }
    }

    //render text
    txtDisplay->Blit( GetX( ), GetY( ) + BOUNDING_RECT_OFFSET, target, &txt_clip );
}


/**
 * Updates the Text Box.
 */

void TextBox::Update()
{
}


/**
 * This routine handles mouse input from the user.
 *
 * @param event: the mouse event
 */

void TextBox::OnMouseEvent(MouseEvent & event)
{
    //if a mouse button was clicked and it's the left mouse button
    if( event.GetType( ) == MOUSE_CLICKED && event.GetButtonUsed( ) == MBTN_LEFT )
    {
        //get event's coordinates
        int mX, mY;
        event.GetOccurredCoordinates( &mX, &mY );
        //if the Text Box was clicked
        if( IsClicked( mX, mY ) )
        {
            //the Text Box has focus
            m_hasFocus = true;

            //position cursor inside the text box
            m_text->SetCursorFromPoint( mX - GetX( ), mY - GetY( ), m_font );
        }
        else //else
        {
            //the Text Box has not focus
            m_hasFocus = false;
        }
    }
}


/**
 * This routine handles keyboard input from the user
 */

void TextBox::OnKeyboardEvent(KeyboardEvent& event)
{
    SDLKey key = event.GetKey( );
    if( event.GetType( ) == KEY_PRESSED )
    {
	//if the user pressed th left arrow key
	if( key == SDLK_LEFT )
	{
	    //move cursor to the left
	    m_text->MoveCursorLeft( );
	}//if the user pressed the right arrow key
	else if( key == SDLK_RIGHT )
	{
	    //move cursor to the right
	    m_text->MoveCursorRight( );
	}//if the user pressed BACKSPACE
	else if( key == SDLK_BACKSPACE )
	{
	    //delete one character before the cursor
	    m_text->DeleteCharacters( -1 );
	}//if the user pressed DELETE
	else if( key == SDLK_DELETE )
	{
	    //delete one character after the cursor
	    m_text->DeleteCharacters( 1 );
	}//if the user pressed the SHIFT key
	else if( key == SDLK_LSHIFT || key == SDLK_RSHIFT )
	{
	    //caps are enabled by default through SDL, need to capture this
	    //in order for the shift's unicode not to be inserted to the text
	}
	else
	{
	    //if scrolling is disabled
	    if( !m_scrollingEnabled )
	    {
		//insert the character
		m_text->InsertCharacter( ( char ) event.GetUnicode( ) );
		//if the width of the text is greater than the text box's
		int txtWidth, txtHeight;
		m_font->TextSize( m_text->GetCurrentRow( ), txtWidth, txtHeight );
		if( txtWidth > GetWidth( ) )
		{
		    //remove the character
		    m_text->DeleteCharacters( 1 );
		}
		else
		{
		    //move cursor to the right
		    m_text->MoveCursorRight( );
		}
	    }
	    else //else
	    {
		ScrollableText *text = dynamic_cast < ScrollableText* > ( m_text.get( ) );
		//insert character
		m_text->InsertCharacter( ( char ) event.GetUnicode( ), 1 );
		//move cursor to the right
		m_text->MoveCursorRight( );

		int txtWidth, txtHeight;
		m_font->TextSize( text->getViewableRow( ), txtWidth, txtHeight );
		if( txtWidth > GetWidth( ) )
		{
		    text->IncreaseLow( );
		}
		else
		{
		    text->IncreaseHigh( );
		}
	    }
	}
    }
}


/**
 * Based on mouse coordinates, returns whether the Text Box was clicked or not.
 *
 * @param mouseX: mouse's x coordinate
 * @param mouseY: mouse's y coordinate
 */

bool TextBox::IsClicked(int mouseX, int mouseY)
{
    return false;
}

};

